<template>
  <div class="appTemplate">
    <Header :title="'进阶案例-矩阵变换'" :back="true"/>
    <main ref="threeView" class="main"></main>
    <div class="buttonGroup">
      <button @click="animateClick">{{ animateActive?'停止动画':'启动动画' }}</button>
    </div>
    <Footer />
  </div>
</template>

<script setup lang="ts">
import Header from '@/components/Header.vue';
import Footer from '@/components/Footer.vue';
import {onMounted, ref} from "vue";
// 导入整个 three.js核心库
import * as THREE from 'three';
import Stats from '&t/examples/jsm/libs/stats.module';
import { VRButton } from '&t/examples/jsm/webxr/VRButton';
// 导入判断WebGL工具
import WebGL from "@/utils/WebGL";
import { ThreeUtil } from "@/utils/ThreeUtil";
import { Texture } from "three";

THREE.Cache.enabled = true;
// 定义three场景显示区域
const threeView = ref();
const animateActive = ref(true);
let threeUtil: ThreeUtil;
const materials = [];

const animateClick = () => {
  if (animateActive.value) {
    threeUtil.renderer.setAnimationLoop(() => {});
  } else {
    animate();
  }
  animateActive.value = !animateActive.value;
}

onMounted(() => {
  if (WebGL.isWebGLAvailable()) {
    initThree();
    animate();
  } else {
    const warning = WebGL.getWebGLErrorMessage();
    threeView.value.appendChild(warning);
  }
})

const initThree = () => {
  threeUtil = new ThreeUtil();
  threeUtil.items['clock'] = new THREE.Clock();
  // 配置相关
  threeUtil.scene.background = new THREE.Color( 0x101010 );
  const light = new THREE.AmbientLight( 0xffffff, 1 );
  threeUtil.scene.add( light );
  threeUtil.camera.fov = 80;
  threeUtil.camera.near = 1;
  threeUtil.camera.far = 1000;
  threeUtil.scene.add( threeUtil.camera );
  createImage();
  createCanvas();
  threeUtil.renderer.setPixelRatio( window.devicePixelRatio );
  threeUtil.renderer.setSize( threeView.value.offsetWidth, threeView.value.offsetWidth, true );
  threeUtil.renderer.xr.enabled = true;
  threeUtil.renderer.xr.setReferenceSpaceType( 'local' );
  threeView.value.appendChild( threeUtil.renderer.domElement );
  threeView.value.appendChild( VRButton.createButton( threeUtil.renderer ) );
  threeView.value.style.touchAction = "none";
  const stats = new Stats();
  stats.dom.style.position = "absolute";
  threeUtil.items['stats'] = stats;
  threeView.value.appendChild( stats.dom );
}

const createImage = () => {
  const panoSphereGeo = new THREE.SphereGeometry( 6, 256, 256 );
  const panoSphereMat = new THREE.MeshStandardMaterial( {
    side: THREE.BackSide,
    displacementScale: - 4.0
  } );
  threeUtil.items['sphere'] = new THREE.Mesh( panoSphereGeo, panoSphereMat );
  const manager = new THREE.LoadingManager();
  const loader = new THREE.TextureLoader( manager );
  loader.load( '/src/assets/textures/kandao3.jpg', ( texture: Texture ) => {
    texture.colorSpace = THREE.SRGBColorSpace;
    texture.minFilter = THREE.NearestFilter;
    texture.generateMipmaps = false;
    threeUtil.items['sphere'].material.map = texture;
  });
  loader.load( '/src/assets/textures/kandao3_depthmap.jpg', ( depth: Texture ) => {
    depth.minFilter = THREE.NearestFilter;
    depth.generateMipmaps = false;
    threeUtil.items['sphere'].material.displacementMap = depth;
  });
  manager.onLoad = () => {
    threeUtil.scene.add( threeUtil.items['sphere'] );
  };
}

const createCanvas = () => {
  const canvas = document.createElement( 'canvas' );
  const ctx = canvas.getContext( '2d' );
  canvas.width = canvas.height = 128;
  if (ctx) {
    ctx.fillStyle = '#ddd';
    ctx.fillRect( 0, 0, 128, 128 );
    ctx.fillStyle = '#555';
    ctx.fillRect( 0, 0, 64, 64 );
    ctx.fillStyle = '#999';
    ctx.fillRect( 32, 32, 32, 32 );
    ctx.fillStyle = '#555';
    ctx.fillRect( 64, 64, 64, 64 );
    ctx.fillStyle = '#777';
    ctx.fillRect( 96, 96, 32, 32 );
  }
  threeUtil.items['mapBg'] = new THREE.CanvasTexture( canvas );
  threeUtil.items['mapBg'].colorSpace = THREE.SRGBColorSpace;
  threeUtil.items['mapBg'].wrapS = threeUtil.items['mapBg'].wrapT = THREE.RepeatWrapping;
  threeUtil.items['mapBg'].repeat.set( 64, 32 );
  threeUtil.scene.background = threeUtil.items['mapBg'];
  const src = [
    { name: 'Zero', constant: THREE.ZeroFactor },
    { name: 'One', constant: THREE.OneFactor },
    { name: 'SrcColor', constant: THREE.SrcColorFactor },
    { name: 'OneMinusSrcColor', constant: THREE.OneMinusSrcColorFactor },
    { name: 'SrcAlpha', constant: THREE.SrcAlphaFactor },
    { name: 'OneMinusSrcAlpha', constant: THREE.OneMinusSrcAlphaFactor },
    { name: 'DstAlpha', constant: THREE.DstAlphaFactor },
    { name: 'OneMinusDstAlpha', constant: THREE.OneMinusDstAlphaFactor },
    { name: 'DstColor', constant: THREE.DstColorFactor },
    { name: 'OneMinusDstColor', constant: THREE.OneMinusDstColorFactor },
    { name: 'SrcAlphaSaturate', constant: THREE.SrcAlphaSaturateFactor }
  ];
  const dst = [
    { name: 'Zero', constant: THREE.ZeroFactor },
    { name: 'One', constant: THREE.OneFactor },
    { name: 'SrcColor', constant: THREE.SrcColorFactor },
    { name: 'OneMinusSrcColor', constant: THREE.OneMinusSrcColorFactor },
    { name: 'SrcAlpha', constant: THREE.SrcAlphaFactor },
    { name: 'OneMinusSrcAlpha', constant: THREE.OneMinusSrcAlphaFactor },
    { name: 'DstAlpha', constant: THREE.DstAlphaFactor },
    { name: 'OneMinusDstAlpha', constant: THREE.OneMinusDstAlphaFactor },
    { name: 'DstColor', constant: THREE.DstColorFactor },
    { name: 'OneMinusDstColor', constant: THREE.OneMinusDstColorFactor }
  ];
  const geo1 = new THREE.PlaneGeometry( 100, 100 );
  const geo2 = new THREE.PlaneGeometry( 100, 25 );
  const texture = new THREE.TextureLoader().load( '/src/assets/textures/lensflare/lensflare0_alpha.png' );
  texture.colorSpace = THREE.SRGBColorSpace;
  for ( let i = 0; i < dst.length; i ++ ) {
    const blendDst = dst[ i ];
    for ( let j = 0; j < src.length; j ++ ) {
      const blendSrc = src[ j ];
      const material = new THREE.MeshBasicMaterial( { map: texture } );
      material.transparent = true;
      material.blending = THREE.CustomBlending;
      material.blendSrc = blendSrc.constant;
      material.blendDst = blendDst.constant;
      material.blendEquation = THREE.AddEquation;
      const x = ( j - src.length / 2 ) * 110;
      const z = 0;
      const y = ( i - dst.length / 2 ) * 110 + 50;
      const mesh = new THREE.Mesh( geo1, material );
      mesh.position.set( x, - y, z );
      mesh.matrixAutoUpdate = false;
      mesh.updateMatrix();
      threeUtil.scene.add( mesh );
      materials.push( material );
    }
  }
  for ( let j = 0; j < src.length; j ++ ) {
    const blendSrc = src[ j ];
    const x = ( j - src.length / 2 ) * 110;
    const z = 0;
    const y = ( 0 - dst.length / 2 ) * 110 + 50;
    const mesh = new THREE.Mesh( geo2, generateLabelMaterial( blendSrc.name, 'rgba( 0, 150, 0, 1 )' ) );
    mesh.position.set( x, - ( y - 70 ), z );
    mesh.matrixAutoUpdate = false;
    mesh.updateMatrix();
    threeUtil.scene.add( mesh );
  }
  for ( let i = 0; i < dst.length; i ++ ) {
    const blendDst = dst[ i ];
    const x = ( 0 - src.length / 2 ) * 110 - 125;
    const z = 0;
    const y = ( i - dst.length / 2 ) * 110 + 165;
    const mesh = new THREE.Mesh( geo2, generateLabelMaterial( blendDst.name, 'rgba( 150, 0, 0, 1 )' ) );
    mesh.position.set( x, - ( y - 120 ), z );
    mesh.matrixAutoUpdate = false;
    mesh.updateMatrix();
    threeUtil.scene.add( mesh );
  }
}

const generateLabelMaterial = ( text: string, bg: string ) => {
  const canvas = document.createElement( 'canvas' );
  const ctx = canvas.getContext( '2d' );
  canvas.width = 128;
  canvas.height = 32;
  if (ctx) {
    ctx.fillStyle = bg;
    ctx.fillRect( 0, 0, 128, 32 );
    ctx.fillStyle = 'white';
    ctx.font = 'bold 11pt arial';
    ctx.fillText( text, 8, 22 );
  }
  const map = new THREE.CanvasTexture( canvas );
  map.colorSpace = THREE.SRGBColorSpace;
  const material = new THREE.MeshBasicMaterial( { map: map, transparent: true } );
  return material;

}


// 渲染
const randerer = () => {
  if ( !threeUtil.renderer.xr.isPresenting ) {
    const time = threeUtil.items['clock'].getElapsedTime();
    threeUtil.items['sphere'].rotation.y += 0.001;
    threeUtil.items['sphere'].position.x = Math.sin( time ) * 0.2;
    threeUtil.items['sphere'].position.z = Math.cos( time ) * 0.2;
  }
  const time = Date.now() * 0.00025;
  const ox = ( time * - 0.01 * threeUtil.items['mapBg'].repeat.x ) % 1;
  const oy = ( time * - 0.01 * threeUtil.items['mapBg'].repeat.y ) % 1;
  threeUtil.items['mapBg'].offset.set( ox, oy );
  threeUtil.renderer.render( threeUtil.scene, threeUtil.camera );
}

const animate = () => {
  threeUtil.renderer.setAnimationLoop(() => {
    randerer();
    threeUtil.items['stats'].update();
  })
}
</script>

<style scoped>

</style>